#include <iostream>
#include <string>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>

static int cnt = 0; // 把cnt放成全局，这样捕捉函数才能用cnt


void Count(int cnt)
{
    while(cnt)
    {
        printf("cnt: %2d\r",cnt--);//将数字按宽度为2，采用右对齐方式输出，若数据位数不到2位，则用空格字符进行补齐。\r是回车输入，回到首行。
        fflush(stdout);
        sleep(1);
    }
}


void handler(int signo)
{
    std::cout << "进程捕捉到了一个信号，信号编号是: " << signo << std::endl;
}

static void Usage(const std::string &proc)//加static修饰，不想把接口暴露在外面
{
    std::cout << "\nUsage: " << proc << " pid signo\n"
              << std::endl;
}
void CatchSigno(int signo)
{
    std::cout << "进程捕捉到了一个信号，信号编号是: " << signo << std::endl;
    std::cout << "cnt: " << cnt << std::endl;
    exit(1);// 打印完cnt的值之后，我们让进程退出即可。

    // Count(20);
    // alarm(1);
}
int main(int argc, char *argv[])
{
    // 核心转储
    while (true)
    {
        int a[10];
        a[10000] = 99;
        // 数组越界，一定会导致程序崩溃吗？答案是不一定。编译器在编译代码的时候，给你函数开辟多大的栈帧和编译器是强相关的，虽然我们数组申请的是40byte
        //但是给main函数开辟的栈帧大小一定不是40byte，所以即使你越界了，但访问的地址依旧有可能是在你的有效栈区里面，所以编译器就没报错。
        //那什么时候编译器会报错呢？除非你访问了一个完全不属于你的空间，已经超出函数栈帧的空间了，这个空间有可能属于其他进程或属于OS，此时OS就能识别出来。

        //所以os在识别越界的问题上，有可能也识别不出来，那就有可能出现数据已经被改掉了，但用户还不知情的情况产生!!! --> 验证C语言阶段数组越界访问检查不出来的情况
    }



    // 4.软件条件异常产生信号
    // 统计1s左右，我们的计算机能将数据累加多少次
    // alarm(1); // 1s后闹钟响了，这就是由于软件条件导致的OS产生14号信号，SIGALRM
    // signal(SIGALRM,CatchSigno);
    // while (true)
    // {
    //     // std::cout << "cnt: " << cnt++ << std::endl;
    //     cnt++;
    // }

    // 3.硬件异常产生信号 --> 信号产生并不一定非得用户显示的给进程发送信号，有时候信号在OS内部会自动的产生!
    // signal(SIGFPE, CatchSigno);
    // int a = 10;//放到死循环外面依旧是疯狂捕捉信号，这是咋回事捏？
    // a /= 0;
    // signal(SIGSEGV, CatchSigno);
    // while (true)
    // {
    //     std::cout << "我正在运行中....." << std::endl;
    //     sleep(1);
    //     // int a = 10;
    //     // a /= 0; // 发生除0错误

    //     int *p = nullptr;
    //     p = nullptr;// 不会报错
    //     *p = 100;// 进程不会让你访问0号地址的四个字节的空间的，linux应该是8字节。
    // }


    // int cnt = 0;
    // while(cnt < 10)
    // {
    //     printf("cnt: %d\n",cnt++);
    //     sleep(1);
    //     // if(cnt >= 5) raise(3);
    //     if(cnt >= 5) abort();
    // }

    // 2.通过系统调用发送信号
    // kill(pid, signo) --> 可以向 任意进程 发送 任意 信号
    // raise(signo) --> 给 自己 发送 任意的 信号 <=> kill(getpid(), 任意信号)
    // abort(void) --> 给 自己 发送 指定的 信号 <=> kill(getpid(), SIGABRT)
    // ./myprocess pid signo
    // if (argc != 3)
    // {
    //     Usage(argv[0]);
    //     exit(1);
    // }
    // // 用户在命令行输入的时候，参数都会被转为字符串，所以我们需要转成整数
    // pid_t pid = atoi(argv[1]); // Ascii 转 Integer
    // int signo = atoi(argv[2]);

    // int n = kill(pid, signo);
    // if(n != 0) perror("kill");

    // 这里是signal函数的调用，不是handler函数的调用。这里仅仅只是设置了进程捕捉到2号信号时的自定义行为，只有当进程收到对应的信号的时候，handler方法才会被调用!
    // signal(9, handler); //9号信号无法被捕捉，即使你这么做了，OS也不会响应
    // signal(2, handler);
    // while (true)
    // {
    //     printf("我是一个进程,我的pid是: %d\n", getpid());
    //     sleep(1);
    // }

    // for (int signo = 1; signo <= 31; signo++)
    // {
    //     signal(signo, CatchSigno); // 我们这里就是设置了当进程捕捉到信号后，进行处理时的自定义行为。如果我们不给进程发信号，CatchSigno也不会被调用。
    // }
    // while (true)
    // {
    //     std::cout<<"我正在运行, 我的pid是 "<<getpid()<<std::endl;
    //     sleep(1);
    // }

    return 0;
}

// while :; do ps axj | head -1 && ps axj | grep mysignal | grep -v grep ; sleep 1; done;